#include "pch.h"
#include "Texture.h"
#include "RendererEngine.h"
namespace mini
{
	CTexture::CTexture()
	{
		mResourceType = Texture;
		ZeroMemory(&mDesc, sizeof(mDesc));
	}


	CTexture::~CTexture()
	{
		SafeRelease(mpShaderResourceView);
		SafeRelease(mpRenderTargetView);
		SafeRelease(mpTexture2D);
		SafeRelease(m_sampleState);
	}

	bool CTexture::initialiseShaderResourceView()
	{
		//// Load the texture in.
		//CDevice* pDevice = CRendererEngine::getSinglePtr()->getDevice();
		//mpDeviceContext = pDevice->getDeviceContext();
		//// Create the texture sampler state.
		//HRESULT result = pDevice->getDevice()->CreateSamplerState(pSampler, &m_sampleState);
		//if (FAILED(result))
		//{
		//	return false;
		//}
		if (mpShaderResourceView == nullptr)
		{
			initialise();
		}
		return true;
	}


	std::vector<UINT8> CTexture::saveAlpha(INT32 x, INT32 y, INT32 width, INT32 height)
	{
		std::vector<UINT8> alpha;
		D3D11_MAPPED_SUBRESOURCE MappedResource;
		CDevice* pDevice = CRendererEngine::getSinglePtr()->getDevice();
		D3D11_TEXTURE2D_DESC description;
		mpTexture2D->GetDesc(&description);
		description.BindFlags = 0;
		description.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
		description.Usage = D3D11_USAGE_STAGING;
		UINT32 Width = width;
		UINT32 Height = height;
		UINT32 X = x * 4;
		UINT32 Y = y + 4;
		ID3D11Texture2D* texTemp = NULL;
		HRESULT hr = pDevice->getDevice()->CreateTexture2D(&description, NULL, &texTemp);
		if (S_OK == hr)
		{
			pDevice->getDeviceContext()->CopyResource(texTemp, mpTexture2D);
			hr = pDevice->getDeviceContext()->Map(texTemp, 0, D3D11_MAP_READ, 0, &MappedResource);
			byte* pRow = (byte*)MappedResource.pData;
			int offset = 0;
			for (UINT32 i = y; i < Height; ++i)
			{
				for (UINT32 j = x; j < Width; ++j)
				{
					alpha.push_back(pRow[offset + j * 4 + 3]);
				}
				offset += MappedResource.RowPitch;
			}
		}
		else
		{
			texTemp->Release();
			texTemp = NULL;
		}
		pDevice->getDeviceContext()->Unmap(texTemp, 0);
		SafeRelease(texTemp);
		return alpha;
	}

	std::vector<UINT8> CTexture::saveAlpha()
	{
		return saveAlpha(0, 0, mDesc.Width, mDesc.Height);
	}

	TextureType CTexture::getTextureType()
	{
		return mType;
	}

	void CTexture::setSampleState(ID3D11SamplerState* pSampleState)
	{
		if (m_sampleState != pSampleState)
		{
			SafeRelease(m_sampleState);
		}
		m_sampleState = pSampleState;
		m_sampleState->AddRef();
	}

	void CTexture::setDeviceContext(DeviceContext* pDeviceContext)
	{
		mpDeviceContext = pDeviceContext;
	}

	bool CTexture::isSamplerStateEmpty()
	{
		return nullptr == m_sampleState ? true : false;
	}

	bool CTexture::saveToFile(const std::string saveFile, DWORD nFormat)
	{
		try
		{
			if (nullptr != mpTexture2D)
			{
				CDevice* pDevice = CRendererEngine::getSinglePtr()->getDevice();
			//	CRendererEngine::getSinglePtr()->stopRender();
				D3D11_TEXTURE2D_DESC description;

				mpTexture2D->GetDesc(&description);
				description.BindFlags = 0;
				description.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
				description.Usage = D3D11_USAGE_STAGING;
				ID3D11Texture2D* texTemp = NULL;
				HRESULT hr = pDevice->getDevice()->CreateTexture2D(&description, NULL, &texTemp);
				if (S_OK == hr)
				{
					pDevice->getDeviceContext()->CopyResource(texTemp, mpTexture2D);
					if ("" == saveFile)
					{
						hr = D3DX11SaveTextureToFile(mpDeviceContext, 
													 texTemp,
													 static_cast<D3DX11_IMAGE_FILE_FORMAT>(nFormat),
													 mResourceName.c_str());
					}
					else
					{
						hr = D3DX11SaveTextureToFile(mpDeviceContext,
													 texTemp,
													 static_cast<D3DX11_IMAGE_FILE_FORMAT>(nFormat), 
													 saveFile.c_str());
					}
					if (FAILED(hr))
					{
						throw texTemp;
					}
					SafeRelease(texTemp);
				}
				else
				{
					throw hr;
				}
			//	CRendererEngine::getSinglePtr()->recoverRender();
			}
			return true;
		}
		catch (ID3D11Texture2D* pTex)
		{
			SafeRelease(pTex);
			CRendererEngine::getSinglePtr()->recoverRender();
			return false;
		}
		catch (...)
		{
			CRendererEngine::getSinglePtr()->recoverRender();
			return false;
		}
	}

	bool CTexture::initialise()
	{
		if (mResourceName.length() > 1)
		{
			// Load the texture in.
			CDevice* pDevice = CRendererEngine::getSinglePtr()->getDevice();
			mpDeviceContext = pDevice->getDeviceContext();
			HRESULT result = D3DX11CreateShaderResourceViewFromFile(pDevice->getDevice(),
				mResourceName.c_str(), nullptr, NULL, &mpShaderResourceView, NULL);
			if (FAILED(result))
			{
				return false;
			}
			mpShaderResourceView->GetResource((ID3D11Resource**)&mpTexture2D);
			//result = D3DX11CreateTextureFromFile(pDevice->getDevice(), mResourceName.c_str(), nullptr, NULL, &pTexture2D, NULL);
			//if (FAILED(result))
			//{
			//	return false;
			//}
			//mpTexture2D = (ID3D11Texture2D*)pTexture2D;
			mpTexture2D->GetDesc(&mDesc);
			return true;
		}
		else
		{
			return false;
		}
	}

	bool CTexture::initialiseRenderTexture(INT32 width, INT32 height, UINT32 nFormat)
	{
		CDevice* pDevice = CRendererEngine::getSinglePtr()->getDevice();
		mpDeviceContext = pDevice->getDeviceContext();
		D3D11_TEXTURE2D_DESC textureDesc;
		HRESULT result;
		D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
		D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;


		// Initialize the render target texture description.
		ZeroMemory(&textureDesc, sizeof(textureDesc));

		// Setup the render target texture description.
		textureDesc.Width = width;
		textureDesc.Height = height;
		textureDesc.MipLevels = 1;
		textureDesc.ArraySize = 1;
		textureDesc.Format = (DXGI_FORMAT)nFormat; 
		textureDesc.SampleDesc.Count = 1;
		textureDesc.Usage = D3D11_USAGE_DEFAULT;
		textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
		textureDesc.CPUAccessFlags = 0;
		textureDesc.MiscFlags = 0;

		// Create the render target texture.
		result = pDevice->getDevice()->CreateTexture2D(&textureDesc, NULL, &mpTexture2D);
		if (FAILED(result))
		{
			return false;
		}
		mDesc = textureDesc;

		// Setup the description of the render target view.
		renderTargetViewDesc.Format = textureDesc.Format;
		renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
		renderTargetViewDesc.Texture2D.MipSlice = 0;

		// Create the render target view.
		result = pDevice->getDevice()->CreateRenderTargetView(mpTexture2D, &renderTargetViewDesc, &mpRenderTargetView);
		if (FAILED(result))
		{
			return false;
		}

		// Setup the description of the shader resource view.
		shaderResourceViewDesc.Format = textureDesc.Format;
		shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
		shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
		shaderResourceViewDesc.Texture2D.MipLevels = 1;

		// Create the shader resource view.
		result = pDevice->getDevice()->CreateShaderResourceView(mpTexture2D, &shaderResourceViewDesc, &mpShaderResourceView);
		if (FAILED(result))
		{
			return false;
		}
		return true;
	}

	void CTexture::clearRenderTargetView(const CPoint4F& color)
	{
		mpDeviceContext->ClearRenderTargetView(mpRenderTargetView, &color.x);
	}

	void CTexture::commitToGpu(DeviceContext* pDeviceContext, INT32 startSlot)
	{
		if (m_sampleState != nullptr)
		{
			pDeviceContext->PSSetSamplers(startSlot, 1, &m_sampleState);
		}
		if (mpShaderResourceView != nullptr)
		{
			pDeviceContext->PSSetShaderResources(startSlot, 1, &mpShaderResourceView);
		}
	}

	UINT32 CTexture::getImageWidth()
	{
		return mDesc.Width;
	}

	UINT32 CTexture::getImageHeight()
	{
		return mDesc.Height;
	}

	UINT32 CTexture::getFormat() const
	{
		return (UINT32)mDesc.Format;
	}

	ID3D11RenderTargetView* CTexture::getRenderTargetView() const
	{
		return mpRenderTargetView;
	}

	ID3D11ShaderResourceView* CTexture::getRenderShaderRV() const
	{
		return mpShaderResourceView;
	}

	void CTexture::copyFrom(ID3D11Texture2D* pTexture, DeviceContext* pContext)
	{
		pContext->CopyResource(mpTexture2D, pTexture);
	}

}
